var VSHADER_SOURCE = `

attribute vec4 a_Position;

attribute vec3 a_normal;

attribute vec2 a_texcoord;

uniform mat4 u_MvpMatrix;
uniform mat4 u_modelInverseTransposeMatrix;
uniform mat4 u_ModelMatrix;

uniform vec3 u_CameraPosition;

varying vec3 v_normal;

uniform vec3 u_lightWorldPosition;

varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;

varying vec2 v_texcoord;

void main() {
  
    gl_Position = u_MvpMatrix * a_Position;
    v_normal = mat3(u_modelInverseTransposeMatrix) * a_normal;
    
    vec3 surfaceWorldPosition = (u_ModelMatrix * a_Position).xyz;
    v_texcoord = a_texcoord;

    v_surfaceToLight = u_lightWorldPosition - surfaceWorldPosition;
    v_surfaceToView = u_CameraPosition - surfaceWorldPosition;
}
`;

// Fragment shader program
var FSHADER_SOURCE = `
#ifdef GL_ES
precision mediump float;
#endif


varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;

varying vec2 v_texcoord;
uniform sampler2D u_texture;

uniform float u_shininess;
uniform vec3 u_lightDirection;
uniform float u_limit;

void main() {
    vec3 normal = normalize(v_normal);
    vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
    vec3 surfaceToViewDirection = normalize(v_surfaceToView);
    vec3 halfVector = normalize(surfaceToLightDirection + surfaceToViewDirection);

    float light = 0.0;
    float specular = 0.0;
    float dotFromDirection = dot(surfaceToLightDirection, -u_lightDirection);
    if (dotFromDirection >= u_limit) {
      light = dot(normal, surfaceToLightDirection);
      if (light > 0.0) {
        specular = pow(dot(normal, halfVector), u_shininess);
      }
      if (light < 0.0) {
        light = 0.0;
      }
    }
    // light = 1.0;
    vec4 textureColor = texture2D(u_texture, v_texcoord);
    vec3 diffuse = textureColor.xyz * light;
    vec3 ambient = textureColor.xyz * 0.5;
    gl_FragColor = vec4(diffuse.xyz + ambient.xyz, textureColor.a);
    gl_FragColor.rgb += specular;
}
`;

var VSHADER_SOURCE_C = `
attribute vec4 a_Position;
uniform mat4 u_MvpMatrix;

void main() {
  gl_Position = u_MvpMatrix * a_Position;
}
`;

// Fragment shader program
var FSHADER_SOURCE_C = `
void main() {
  gl_FragColor = vec4(1, 1, 1, 1); 
}
`;

function radToDeg(r) {
  return (r * 180) / Math.PI;
}

function degToRad(d) {
  return (d * Math.PI) / 180;
}

function createSliders() {
  webglLessonsUI.setupSlider("#cameraPositionX", {
    value: cameraPositionX,
    slide: updateX,
    min: -30,
    max: 30,
  });

  function updateX(event, ui) {
    cameraPositionX = ui.value;
    draw();
  }

  webglLessonsUI.setupSlider("#cameraPositionY", {
    value: cameraPositionY,
    slide: updateY,
    min: -30,
    max: 30,
  });

  function updateY(event, ui) {
    cameraPositionY = ui.value;
    draw();
  }

  webglLessonsUI.setupSlider("#cameraPositionZ", {
    value: cameraPositionZ,
    slide: updateZ,
    min: -30,
    max: 30,
  });

  function updateZ(event, ui) {
    cameraPositionZ = ui.value;
    draw();
  }

  webglLessonsUI.setupSlider("#rotation_pos_x", {
    value: rotation_pos_x,
    slide: update_rotation_pos_x,
    min: -180,
    max: 180,
    step: 0.1,
  });

  function update_rotation_pos_x(event, ui) {
    rotation_pos_x = ui.value;
    draw();
  }

  webglLessonsUI.setupSlider("#rotation_pos_y", {
    value: rotation_pos_y,
    slide: update_rotation_pos_y,
    min: -180,
    max: 180,
    step: 0.1,
  });

  function update_rotation_pos_y(event, ui) {
    rotation_pos_y = ui.value;
    draw();
  }

  webglLessonsUI.setupSlider("#rotation", {
    value: rotation,
    slide: updateRotation,
    min: -180,
    max: 180,
  });
  function updateRotation(event, ui) {
    rotation = ui.value;
    draw();
  }

  webglLessonsUI.setupSlider("#shinness", {
    value: shinness,
    slide: updateShinness,
    min: 0,
    max: 1000,
  });
  function updateShinness(event, ui) {
    shinness = ui.value;
    draw();
  }
}

webglLessonsUI.setupSlider("#limit", {
  value: limit,
  slide: updateLimit,
  min: -1,
  max: 1,
  precision: 2,
  step: 0.001,
});
function updateLimit(event, ui) {
  limit = ui.value;
  draw();
}

var imageBox = new Image();
var imageFloor = new Image();
function main() {
  createSliders();
  imageBox.src = "./icon.png";
  imageBox.addEventListener("load", function () {
    imageFloor.src = "./floor.png";
    imageFloor.addEventListener("load", function () {
      draw();
    });
  });
}

function draw() {
  // Retrieve <canvas> element
  var canvas = document.getElementById("webgl");

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log("Failed to get the rendering context for WebGL");
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log("Failed to intialize shaders.");
    return;
  }

  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log("Failed to set the vertex information");
    return;
  }

  var u_ModelMatrix = gl.getUniformLocation(gl.program, "u_ModelMatrix");

  // Get the storage locations of uniform variables and so on
  var u_MvpMatrix = gl.getUniformLocation(gl.program, "u_MvpMatrix");

  var u_lightWorldPosition = gl.getUniformLocation(
    gl.program,
    "u_lightWorldPosition"
  );

  var texcoordLocation = gl.getAttribLocation(gl.program, "a_texcoord");
  var textureLocation = gl.getUniformLocation(gl.program, "u_texture");

  var u_modelInverseTransposeMatrix = gl.getUniformLocation(
    gl.program,
    "u_modelInverseTransposeMatrix"
  );

  var u_CameraPosition = gl.getUniformLocation(gl.program, "u_CameraPosition");

  var shininessLocation = gl.getUniformLocation(gl.program, "u_shininess");
  var lightDirectionLocation = gl.getUniformLocation(
    gl.program,
    "u_lightDirection"
  );

  {
    var lmat = new Matrix4();
    lmat.lookAt(
      lightPositionX,
      lightPositionY,
      lightPositionZ,
      0,
      0,
      0,
      0,
      1,
      0
    );

    var lightD = new Vector3([
      -lightPositionX,
      -lightPositionY,
      -lightPositionZ,
    ]);

    var rotateMatrix = new Matrix4();
    rotateMatrix.setRotate(rotation_pos_x, 1, 0, 0);
    lightD = rotateMatrix.multiplyVector3(lightD);
    var rotateMatrix = new Matrix4();
    rotateMatrix.setRotate(rotation_pos_y, 0, 1, 0);

    var lightDirection = rotateMatrix.multiplyVector3(lightD);
    lightDirection.normalize();
    console.log("^^^^^^");
    console.log(
      "lightDirection vecter==",
      lightDirection.elements[0],
      lightDirection.elements[1],
      lightDirection.elements[2]
    );

    var xRotate = new Matrix4();
    xRotate.setRotate(rotation_pos_x, 1, 0, 0);
    lmat.multiply(xRotate);
    var yRotate = new Matrix4();
    yRotate.setRotate(rotation_pos_y, 0, 1, 0);
    lmat.multiply(yRotate);
  }
  // console.log("123", -lmat.elements[8], -lmat.elements[9], -lmat.elements[10]);
  console.log(
    "lightDirection matrix",
    -lmat.elements[2],
    -lmat.elements[6],
    -lmat.elements[10]
  );
  console.log("vvvvvvvv");
  gl.uniform3f(
    lightDirectionLocation,
    lightDirection.elements[0],
    lightDirection.elements[1],
    lightDirection.elements[2]
  );

  var limitLocation = gl.getUniformLocation(gl.program, "u_limit");

  // Create a texture.
  var texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  // Fill the texture with a 1x1 blue pixel.
  gl.texImage2D(
    gl.TEXTURE_2D,
    0,
    gl.RGBA,
    1,
    1,
    0,
    gl.RGBA,
    gl.UNSIGNED_BYTE,
    new Uint8Array([0, 0, 255, 255])
  );

  // provide texture coordinates for the rectangle.
  var texcoordBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
  // Set Texcoords.
  gl.bufferData(
    gl.ARRAY_BUFFER,
    new Float32Array(texcoordArray),
    gl.STATIC_DRAW
  );
  // Turn on the texcoord attribute
  gl.enableVertexAttribArray(texcoordLocation);

  gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);

  gl.uniform1i(textureLocation, 0);

  gl.uniform3f(
    u_lightWorldPosition,
    lightPositionX,
    lightPositionY,
    lightPositionZ
  );

  var modelMatrix = new Matrix4();
  modelMatrix.setRotate(rotation, 0, 1, 0);
  gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

  // Calculate the view projection matrix
  var mvpMatrix = new Matrix4(); // Model view projection matrix
  mvpMatrix.setPerspective(30, canvas.width / canvas.height, 1, 100);

  mvpMatrix.lookAt(
    cameraPositionX,
    cameraPositionY,
    cameraPositionZ,
    0,
    0,
    0,
    0,
    1,
    0
  );
  mvpMatrix.multiply(modelMatrix);
  // Pass the model view projection matrix to the variable u_MvpMatrix
  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

  var modelInverseTransposeMatrix = new Matrix4(); // Transformation matrix for normals

  // Pass the matrix to transform the normal based on the model matrix to u_modelInverseTransposeMatrix
  modelInverseTransposeMatrix.setInverseOf(modelMatrix);
  modelInverseTransposeMatrix.transpose();

  gl.uniformMatrix4fv(
    u_modelInverseTransposeMatrix,
    false,
    modelInverseTransposeMatrix.elements
  );

  var cameraPosition = new Vector3([
    cameraPositionX,
    cameraPositionY,
    cameraPositionZ,
  ]);
  gl.uniform3fv(u_CameraPosition, cameraPosition.elements);
  gl.uniform1f(shininessLocation, shinness);
  gl.uniform1f(limitLocation, Math.cos(limit));
  // Set the clear color and enable the depth test
  gl.clearColor(0.8, 0.8, 0.8, 1);
  gl.enable(gl.DEPTH_TEST);

  // Clear color and depth buffer
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  // Flip the imageBox's y axis
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
  // Now that the imageBox has loaded make copy it to the texture.
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageBox);
  gl.generateMipmap(gl.TEXTURE_2D);
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);


  /// 地板 ====================================================
  /// 地板 ====================================================
  /// 地板 ====================================================
  /// 地板 ====================================================
  /// 地板 ====================================================
  var modelMatrix = new Matrix4();
  modelMatrix.setTranslate(0, -2, 0).scale(5, 1, 5);
  gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
  // Calculate the view projection matrix
  var mvpMatrix = new Matrix4(); // Model view projection matrix
  mvpMatrix.setPerspective(30, canvas.width / canvas.height, 1, 100);

  mvpMatrix.lookAt(
    cameraPositionX,
    cameraPositionY,
    cameraPositionZ,
    0,
    0,
    0,
    0,
    1,
    0
  );
  mvpMatrix.multiply(modelMatrix);
  // Pass the model view projection matrix to the variable u_MvpMatrix
  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

  // Indices of the vertices
  var indices = new Uint8Array(indicesFloorArray);

  // Write the indices to the buffer object
  var indexBuffer = gl.createBuffer();
  if (!indexBuffer) {
    console.log("Failed to create the buffer object");
    return false;
  }
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  // Create a texture.
  var texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  // Fill the texture with a 1x1 blue pixel.
  gl.texImage2D(
    gl.TEXTURE_2D,
    0,
    gl.RGBA,
    1,
    1,
    0,
    gl.RGBA,
    gl.UNSIGNED_BYTE,
    new Uint8Array([0, 0, 255, 255])
  );
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
  // Now that the imageBox has loaded make copy it to the texture.
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(
    gl.TEXTURE_2D,
    0,
    gl.RGBA,
    gl.RGBA,
    gl.UNSIGNED_BYTE,
    imageFloor
  );
  gl.generateMipmap(gl.TEXTURE_2D);
  gl.drawElements(gl.TRIANGLES, indicesFloorArray.length, gl.UNSIGNED_BYTE, 0); 




  /// 灯 ====================================================
  /// 灯 ====================================================
  /// 灯 ====================================================
  /// 灯 ====================================================
  var vertices = new Float32Array(GetVerticesArray());

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE_C, FSHADER_SOURCE_C)) {
    console.log("Failed to intialize shaders.");
    return;
  }
  if (!initArrayBuffer(gl, "a_Position", vertices, 3)) return -1;

  // Get the storage locations of uniform variables and so on
  var u_MvpMatrix = gl.getUniformLocation(gl.program, "u_MvpMatrix");

  // Indices of the vertices
  var indices = new Uint8Array(indicesArray);

  // Write the indices to the buffer object
  var indexBuffer = gl.createBuffer();
  if (!indexBuffer) {
    console.log("Failed to create the buffer object");
    return false;
  }
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);


  var modelMatrix = new Matrix4();

  // Calculate the model matrix
  modelMatrix = modelMatrix
    .setTranslate(lightPositionX, lightPositionY, lightPositionZ)
    .scale(0.2, 0.2, 0.2);
  // modelMatrix
  // Calculate the view projection matrix
  var mvpMatrix = new Matrix4(); // Model view projection matrix
  mvpMatrix.setPerspective(30, canvas.width / canvas.height, 1, 100);
  mvpMatrix.lookAt(
    cameraPositionX,
    cameraPositionY,
    cameraPositionZ,
    0,
    0,
    0,
    0,
    1,
    0
  );
  mvpMatrix.multiply(modelMatrix);
  // Pass the model view projection matrix to the variable u_MvpMatrix
  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}

function initVertexBuffers(gl) {
  // Create a cube
  //    v6----- v5
  //   /|      /|
  //  v1------v0|
  //  | |     | |
  //  | |v7---|-|v4
  //  |/      |/
  //  v2------v3
  // Coordinates
  var vertices = new Float32Array(GetVerticesArray());

  // Colors
  var colors = new Float32Array(colorsArray);

  // Normal
  var normals = new Float32Array(normalsArray);

  // Write the vertex property to buffers (coordinates, colors and normals)
  if (!initArrayBuffer(gl, "a_Position", vertices, 3)) return -1;
  if (!initArrayBuffer(gl, "a_normal", normals, 3)) return -1;

  // Unbind the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  // Indices of the vertices
  var indices = new Uint8Array(indicesArray);

  // Write the indices to the buffer object
  var indexBuffer = gl.createBuffer();
  if (!indexBuffer) {
    console.log("Failed to create the buffer object");
    return false;
  }
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  return indices.length;
}

function initArrayBuffer(gl, attribute, data, num) {
  // Create a buffer object
  var buffer = gl.createBuffer();
  if (!buffer) {
    console.log("Failed to create the buffer object");
    return false;
  }
  // Write date into the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  // Assign the buffer object to the attribute variable
  var a_attribute = gl.getAttribLocation(gl.program, attribute);
  if (a_attribute < 0) {
    console.log("Failed to get the storage location of " + attribute);
    return false;
  }
  gl.vertexAttribPointer(a_attribute, num, gl.FLOAT, false, 0, 0);
  // Enable the assignment of the buffer object to the attribute variable
  gl.enableVertexAttribArray(a_attribute);

  return true;
}
